﻿using StarPizza.Model.BaseModel;
using StarPizza.Model.Core;
using StarPizza.Model.User;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace StarPizza.ViewModel.Core
{
    public abstract class IngredientLister<TIngredient, TIngredientVM> : INotifyPropertyChanged
        where TIngredient : Ingredient
        where TIngredientVM : IngredientVM
    {
        public event PropertyChangedEventHandler PropertyChanged;
        protected void OnPropertyChanged(string propertyName)
        {
            if (this.PropertyChanged != null)
            {
                this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        protected IIngredientResource ingredientResource;
        IUserModeManager userModeManager;
        
        private TIngredientVM selectedIngredientVM;
        public TIngredientVM SelectedIngredientVM
        {
            get { return selectedIngredientVM; }
            set
            {
                if (value != selectedIngredientVM)
                {
                    selectedIngredientVM = value;
                    OnPropertyChanged("SelectedIngredientVM");
                }
                if (IngredientVMSelected != null)
                    IngredientVMSelected(selectedIngredientVM);
            }
        }
        
        public event Action<TIngredientVM> IngredientVMSelected;
        public event Action<TIngredientVM> IngredientVMChosen;
        
        protected Dictionary<TIngredient, TIngredientVM> mappedIngredientVMs;
        public ObservableCollection<TIngredientVM> IngredientVMs { get; set; }

        public IngredientLister(IIngredientResource ingredientResource, IUserModeManager userModeManager)
        {
            this.ingredientResource = ingredientResource;
            this.userModeManager = userModeManager;
            mappedIngredientVMs = new Dictionary<TIngredient, TIngredientVM>();
            IngredientVMs = new ObservableCollection<TIngredientVM>();
            userModeManager.UserModeChanged += userModeManager_UserModeChanged;
        }
        void userModeManager_UserModeChanged(UserMode newUIMode)
        {
            if (newUIMode == UserMode.Client)
            {
                HideIngredientsOutOfStock();
            }
            else
            {
                foreach (var ingredientVM in mappedIngredientVMs.Values)
                {
                    if (!IngredientVMs.Contains(ingredientVM))
                    {
                        IngredientVMs.Add(ingredientVM);
                    }
                }
            }
        }
        protected abstract TIngredientVM CreateIngredientVM(TIngredient ingredient);
        TIngredientVM PrepareIngredient(TIngredient ingredient)
        {
            var ingredientVM = CreateIngredientVM(ingredient);
            mappedIngredientVMs[ingredient] = ingredientVM;
            ingredientVM.Amount = ingredientResource.GetAmount(ingredient);
            return ingredientVM;
        }
        protected void RegisterIngredient(TIngredient ingredient)
        {
            bool areWeInClientMode = userModeManager.UserMode == UserMode.Client;
            var ingredientVM = PrepareIngredient(ingredient);
            if (!areWeInClientMode || ingredientResource.IsIngredientAvailable(ingredient))
            {
                IngredientVMs.Add(ingredientVM);
            }
        }
        protected abstract IEnumerable<TIngredient> GetSpecificIngredients();
        protected void SetupIngredients()
        {
            foreach (var ingredient in GetSpecificIngredients())
            {
                RegisterIngredient(ingredient);
            }
        }
        void HideIngredientsOutOfStock()
        {
            foreach (var kvp in mappedIngredientVMs)
            {
                if (!ingredientResource.IsIngredientAvailable(ingredient: kvp.Key))
                {
                    OutOfIngredient(ingredientVM: kvp.Value);
                }
            }
        }
        void OutOfIngredient(TIngredientVM ingredientVM)
        {
            if (userModeManager.UserMode == UserMode.Client)
            {
                IngredientVMs.Remove(ingredientVM);
            }
        }
        protected void IngredientStorageChanged(TIngredient ingredient, int diff, int newAmount)
        {
            var ingredientVM = mappedIngredientVMs[ingredient];
            ingredientVM.Amount = newAmount;
            if (newAmount == 0)
            {
                OutOfIngredient(ingredientVM);
            }
            else
            {
                IngredientBecameAvailable(ingredientVM);
            }
        }
        void IngredientBecameAvailable(TIngredientVM ingredientVM)
        {
            if (!IngredientVMs.Contains(ingredientVM))
            {
                IngredientVMs.Add(ingredientVM);
            }
        }
        public void ChooseSelectedIngredientVM()
        {
            if (IngredientVMChosen != null)
                IngredientVMChosen(SelectedIngredientVM);
        }
    }
}
